package com.hfzy.ihk.web.ivr.biz;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.hfzy.ihk.common.eventSocket.enums.EventName;
import com.hfzy.ihk.common.facade.enums.call.CommitTo;
import com.hfzy.ihk.common.util.date.DateUtils;
import com.hfzy.ihk.common.util.string.StringTools;
import com.hfzy.ihk.common.util.string.StringUtil;
import com.hfzy.ihk.common.util.uuid.base64uuid.UUIDs;
import com.hfzy.ihk.facade.ivr.enums.CallTypeEnum;
import com.hfzy.ihk.facade.ivr.enums.MsgTypeEnum;
import com.hfzy.ihk.facade.ivr.vo.FreeswitchCallState;
import com.hfzy.ihk.web.ivr.constant.*;
import com.hfzy.ihk.web.ivr.context.DisruptorInitBean;
import com.hfzy.ihk.web.ivr.context.EventSocketInitBean;
import com.hfzy.ihk.web.ivr.support.callin.event.FreeswitchCallStateEvent;
import com.hfzy.ihk.web.ivr.support.disruptor.DisruptorSupport;
import com.hfzy.ihk.web.ivr.vo.VoiceRTASRReportVo;
import com.lmax.disruptor.RingBuffer;
import org.freeswitch.esl.client.transport.event.EslEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * @Auther: Administrator
 * @Date: 2018/10/31 09:19
 * @Description:
 */
@Lazy
@Component
public class FreeswitchAsrCallStateBiz extends DisruptorSupport<FreeswitchCallState, FreeswitchCallStateEvent>{

    private static final Logger logger = LoggerFactory.getLogger(FreeswitchAsrCallStateBiz.class);

    @Autowired
    EventSocketInitBean eslInitBean;

    @Autowired
    PushDataToAnalysis pushDataToAnalysis;

    @Autowired
    PushDataToBrowserBiz pushDataToBrowserBiz;

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    protected RingBuffer<FreeswitchCallStateEvent> useRingBuffer(FreeswitchCallState data) {

        int index = 0;
        RingBuffer ringBuffer = null;

        String useType = "ivr --> Freeswitch中间状态";

        String sessionId = data.getEvent().getEventHeaders().get("Channel-Call-UUID");

        if(StringTools.isEmpty(sessionId)){
            sessionId = data.getEvent().getEventHeaders().get("sessionId");
        }

        if (StringTools.isEmpty(sessionId))
            return null;

        //session 做hash 判断应该进哪一个队列
        index = sessionId.hashCode() % PublicConfig.ALIYUN_CALL_STATE_BUFFER_NUM_SIZE;

        ringBuffer = DisruptorInitBean.getRingBufferFreeswitchCallState(Math.abs(index));

        checkRingBufferUse(ringBuffer, useType);
        return ringBuffer;

    }


    /**
     * 处理状态消息
     *
     * @param
     */

    public void dealCallState(EslEvent event) {
/*        //TODO
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }*/

        FreeswitchCallState freeswitchCallState=initFreeswitch(event);

        if (StringTools.isEmpty(freeswitchCallState)) {
            return;
        }

        logger.debug("putInRingBuffer:{}",JSON.toJSONString(freeswitchCallState));
        putInRingBuffer(freeswitchCallState);

    }

    /**
     * 初始化freeswitchState
     * @param event
     * @return
     */
    public FreeswitchCallState initFreeswitch(EslEvent event) {

        FreeswitchCallState callState=new FreeswitchCallState(event);

        if (StringUtil.isNotNull(event)) {
            //取得freeswitch所有头信息
            Map<String, String> eventHeaders = event.getEventHeaders();
            //获取sessionId,事件名,当前号码
            String sessionId = eventHeaders.get("Channel-Call-UUID");
            /**自定义的消息中sessionId的值就是sessionId字段，而没有Channel-Call-UUID*/
            if(StringTools.isEmpty(sessionId)){
                sessionId = eventHeaders.get("sessionId");
            }
            if(StringTools.isEmpty(sessionId)){
                logger.error("ivr initFreeswitch failed! sessionId读取失败！");
            }

            String eventName = eventHeaders.get("Event-Name");

            //redisKey
            String redisKey = RedisKey.CALL_HISTORY_RPIX + sessionId;
            //TODO
            //从redis获取主叫号码和呼叫来源（呼入或者呼出）
            String mainCallPhone = (String) redisTemplate.opsForHash().get(redisKey, RedisFields.CALL_HISTORY_MAIN_CALL_PHONE);
            String commitTo = (String) redisTemplate.opsForHash().get(redisKey, RedisFields.CALL_HISTORY_COMMIT_TO);

            //开始进行freeswitch状态设置

            callState.setOut_id(sessionId);
            callState.setStatus_code(eventName);
            callState.setMsgType(AliyunAkInfo.VOICECALLREPORT);
            callState.setStatus_time(event.getEventDateLocal());
            callState.setCaller(mainCallPhone);

            String destinationNumber=eventHeaders.get("variable_sip_contact_user");
            callState.setDestinationNumber(destinationNumber);
            String targetCallPhone = (String) redisTemplate.opsForHash().get(redisKey, RedisFields.CALL_HISTORY_TARGET_CALL_PHONE);
            callState.setCallee(targetCallPhone);

            //双呼7秒没接通，转入单呼流程(sip_h_goto_robot-->拨号计划设置)
            if(CommitTo.FREESWITCH.getValue().equals(commitTo)&&eventHeaders.containsKey("variable_sip_h_goto_robot")){
                redisTemplate.opsForHash().put(redisKey, RedisFields.CALL_HISTORY_COMMIT_TO,CommitTo.ASR_FREESWITCH.getValue());
            }

            switch (EventName.fromName(eventName)){

                case CHANNEL_HANGUP_COMPLETE://挂断事件
                    //双呼
                    if(!eventHeaders.containsKey("variable_sip_h_goto_robot")){

                        //只将第一个CHANNEL_HANGUP_COMPLETE作为话单状态返回
                        // (呼入如果有被叫7秒没有接通，会触发挂断事件，这个事件处理没有意义，所以后面会出现variable_sip_h_goto_robot字段标识进入单呼再来处理)
                        if (!redisTemplate.hasKey(RedisKey.CALL_HISTORY_RPIX + sessionId+"_CDR")){

                            redisTemplate.opsForValue().set(RedisKey.CALL_HISTORY_RPIX + sessionId+"_CDR","true",1, TimeUnit.DAYS);

                            //当前event返回的时间
                            String endTimeString = DateUtils.stringFromString(event.getEventDateLocal(), DateUtils.DATE_FORMAT_DATETIME_SSS);

                            //转化为long类型,待会要计算通话时长
                            long endTime = DateUtils.getDateFromString(endTimeString,DateUtils.DATE_FORMAT_DATETIME_SSS).getTime();

                            //取出主叫应答时间
                            String mainStartTimeString = (String)redisTemplate.opsForHash().get(redisKey, RedisFields.CALL_HISTORY_MAIN_CALL_ANSWERED_TIME);

                            if (mainStartTimeString!=null){//假如主叫有应答,计算主叫通话时长,设置相关参数

                                //转化为long类型,要计算通话时长
                                long mainStartTime = DateUtils.getDateFromString(mainStartTimeString, "yyyyMMddHHmmssSSS").getTime();
                                callState.setA_duration(String.valueOf((endTime-mainStartTime)/1000));
                                callState.setA_start_time(mainStartTimeString);
                                callState.setA_end_time(endTimeString);
                            }

                            //取出被叫应答时间
                            String targetStartTimeString = (String) redisTemplate.opsForHash().get(redisKey, RedisFields.CALL_HISTORY_TARGET_CALL_ANSWERED_TIME);
                            if (targetStartTimeString!=null){//假如被叫有应答,计算被叫通话时长,设置相关参数

                                //设置为话单返回状态（呼入7秒经纪有接通，才会进行入库，否则直接转入单呼）
                                callState.setMsgType(AliyunAkInfo.VOICEREPORT);

                                //转化为long类型,要计算通话时长
                                long targetStartTime = DateUtils.getDateFromString(targetStartTimeString, "yyyyMMddHHmmssSSS").getTime();

                                callState.setDuration(String.valueOf(( endTime- targetStartTime)/1000));
                                callState.setStart_time(targetStartTimeString);
                                callState.setEnd_time(endTimeString);

                            }
                        }
                    }else{//单呼
                        callState.setMsgType(AliyunAkInfo.VOICEREPORT);

                        //当前event返回的时间
                        String endTimeString = DateUtils.stringFromString(event.getEventDateLocal(), DateUtils.DATE_FORMAT_DATETIME_SSS);

                        //转化为long类型,待会要计算通话时长
                        long endTime = DateUtils.getDateFromString(endTimeString,DateUtils.DATE_FORMAT_DATETIME_SSS).getTime();

                        //取出主叫应答时间
                        String mainStartTimeString = (String)redisTemplate.opsForHash().get(redisKey, RedisFields.CALL_HISTORY_MAIN_CALL_ANSWERED_TIME);

                        if (mainStartTimeString!=null){//假如主叫有应答,计算主叫通话时长,设置相关参数

                            //转化为long类型,要计算通话时长
                            long mainStartTime = DateUtils.getDateFromString(mainStartTimeString, "yyyyMMddHHmmssSSS").getTime();
                            callState.setA_duration(String.valueOf((endTime-mainStartTime)/1000));
                            callState.setA_start_time(mainStartTimeString);
                            callState.setA_end_time(endTimeString);
                        }
                    }

                    break;
            }


        }

        return callState;
    }

    /**-------------------------------自定义消息处理--------------------------------*/

    /**
     * 处理语音识别
     */
    public void handleAlibabaAsr(EslEvent event){

        Map<String, String> eventHeaders = event.getEventHeaders();

        String sessionId = eventHeaders.get("session_id");
        String role = eventHeaders.get("role");

      //单呼不用处理机器人说的话
        String commitTo = (String)redisTemplate.opsForHash().get(RedisKey.CALL_HISTORY_RPIX + sessionId, RedisFields.CALL_HISTORY_COMMIT_TO);
        if(StringTools.isNotNull(commitTo)&& CommitTo.ASR_FREESWITCH.getValue().equals(commitTo)&&StringTools.isNotempty(role)&&"A".equals(role)){
            logger.info("ivr单呼收到语音分析结果:sessionId:{}",sessionId);
            return;
        }else{
            logger.info("ivr收到freeswitch语音分析结果:sessionId:"+sessionId);
        }


        if(StringTools.isEmpty(sessionId)) {
            logger.info("----->消息队列接收ars消息，获取sessionId异常："+JSON.toJSONString(event));
            return ;
        }
        String[] arg = pushDataToAnalysis.readPackageIdAndCallTaskId(sessionId);

        if(arg==null||arg.length<2){

            logger.info("-------------->packageId和callTaskId为null或者只有一个,不继续处理,sessionId:"+sessionId);

            return ;
        }
        logger.info("-------------->获得arg length:"+arg.length);
        //填写推送代码 获取callTaskId 和 packageId
        logger.info("---->packageId:{},callTaskId:{},",arg[0],arg[1]);

        //根据sessionId推送到客户端
        Long start =System.currentTimeMillis();

        Map<String,Map<String,Object>> asrResponse = JSON.parseObject(eventHeaders.get("asr_response"), Map.class);
        VoiceRTASRReportVo voiceRTASRReportVo = pushSpeechAnalysisToBrowser(role,sessionId,(String)(asrResponse.get("payload").get("result")));

        logger.info("pushAsrToBrowser将words推送浏览器 入队时间："+(System.currentTimeMillis()-start));

        //进推送队列推送
        Long start1 =System.currentTimeMillis();
        try {
            String rs =  pushDataToAnalysis.pushAsrData(arg[0], arg[1], voiceRTASRReportVo.getOut_id(),  voiceRTASRReportVo, commitTo, MsgTypeEnum.NORMAL.getValue());

            if(CommitTo.ASR_FREESWITCH.getValue().equals(commitTo)){
                //通过分析播放录音
                ConcurrentHashMap<String, List> recordFileUrlMap = EventSocketInitBean.getRecordFileUrlMap();
                JSONObject jsonObject = JSONObject.parseObject(rs);
                if(StringTools.isNotempty(jsonObject)&&jsonObject.containsKey("recordFileUrl")){
                    List<String> recordFileUrl = JSON.toJavaObject(jsonObject.getJSONArray("recordFileUrl"),List.class);
                    StringBuffer stringBuffer = new StringBuffer();
                    if(recordFileUrlMap.containsKey(sessionId)){//判断当前通话是否有录音还没有播放
                        List list = recordFileUrlMap.get(sessionId);
                        if(StringTools.isNotempty(list)){
                            list.stream().forEach(o -> stringBuffer.append(o.toString()+"!"));
                        }
                    }
                    if(StringTools.isNotempty(recordFileUrl)){
                        for(int i=0;i<recordFileUrl.size();i++){
                            if(i==recordFileUrl.size()-1){
                                stringBuffer.append(recordFileUrl.get(i));
                            }else{
                                stringBuffer.append(recordFileUrl.get(i)+"!");
                            }
                        }
                        if (!eslInitBean.getClient().canSend()) {
                            logger.error("freeswitchl连接已经断开");
                            return;
                        }else{
                            if("true".equals(EventSocketInitBean.getCustomerConnection().get(sessionId+ RedisKey.CUSTOMER_SPEAK_STATE))){//客户正在讲话把这段录音存到本地
                                List list = recordFileUrlMap.get(sessionId);
                                if(list == null)
                                    list = new ArrayList();
                                list.addAll(recordFileUrl);
                                recordFileUrlMap.put(sessionId,list);
                            }else{
                                logger.info("播放语音分析录音文件字符串："+stringBuffer);
                                try {
                                    eslInitBean.getClient().sendBackgroundApiCommand("uuid_playback", sessionId+" file_string://"+stringBuffer);
                                }catch (Exception e){
                                    logger.error("播放语音分析录音文件字符串异常，异常信息：{}",e);
                                }
                                if(jsonObject.containsKey("speaking")&&StringTools.isNotempty(jsonObject.get("speaking"))){
                                    pushSpeechAnalysisToBrowser("A",sessionId,jsonObject.get("speaking").toString());//单呼机器人文字信息发送到客户端
                                }
                                if(StringTools.isNotempty(recordFileUrlMap.get(sessionId))){
                                    recordFileUrlMap.get(sessionId).clear();
                                }
                            }
                        }
                    }
                }
            }
        }catch (Exception e){
            logger.error("--------->推送识别结果到分析服务器异常，sessionId:{}",voiceRTASRReportVo.getOut_id(),e);
        }
        logger.info("将words推送分析服务器入队时间："+(System.currentTimeMillis()-start1));

    }

    public void handleLineBusy(EslEvent event) {

        FreeswitchCallState freeswitchCallState=initFreeswitch(event);

        if (StringTools.isEmpty(freeswitchCallState)) {
            return;
        }
        logger.debug("putInRingBuffer:{}",JSON.toJSONString(freeswitchCallState));

        putInRingBuffer(freeswitchCallState);
    }

    /**
     * 语音识别结束通知语音分析服务器
     * @param event
     */
    public void handleSpeechRecognitionEnd(EslEvent event){

        Map<String, String> eventHeaders = event.getEventHeaders();
        String sessionId = eventHeaders.get("session_id");

        //挂断电话
        try{
            eslInitBean.getClient().sendBackgroundApiCommand("uuid_kill",sessionId);
        }catch (Exception e){
            logger.info("单呼语音结束挂断电话异常，异常信息：{}",e);
        }

        redisTemplate.setEnableTransactionSupport(true);
        redisTemplate.multi();
        redisTemplate.opsForValue().setIfAbsent(RedisKey.IDENTIFICATION_END_STATE+sessionId,"true");
        redisTemplate.expire(RedisKey.IDENTIFICATION_END_STATE+sessionId, RedisTimeOut.CACHE_THREE_DAY, TimeUnit.SECONDS);
        List result = redisTemplate.exec(); // 这里result会返回事务内每一个操作的结果，如果setIfAbsent操作失败后，result[0]会为false。
        if(Boolean.valueOf(result.get(0).toString())){
            String[] packageIdAndCallTaskId = pushDataToAnalysis.readPackageIdAndCallTaskId(sessionId);
            if(packageIdAndCallTaskId!=null&&packageIdAndCallTaskId.length>1) {
                logger.info("ivr----->发送freeswitch通话结束标识到分析服务器sessionId:{}",sessionId);
                try {
                    pushDataToAnalysis.pushAsrData(packageIdAndCallTaskId[0], packageIdAndCallTaskId[1], sessionId,  new VoiceRTASRReportVo(), CallTypeEnum.ASR_CALL.getValue(), MsgTypeEnum.END.getValue());
                }catch (Exception e){
                    logger.error("ivr--------->发送freeswitch通话结束状态到分析服务器异常，sessionId:{}",sessionId,e);
                }
            }
        }
    }

    /**
     * 语音分析推送到浏览器
     * @param role
     * @param sessionId
     * @param voiceText
     * @return
     */
    public VoiceRTASRReportVo pushSpeechAnalysisToBrowser(String role,String sessionId,String voiceText){
        String callType = (String)redisTemplate.opsForHash().get(RedisKey.CALL_HISTORY_RPIX + sessionId, RedisFields.CALL_HISTORY_CALL_TYPE);
        VoiceRTASRReportVo voiceRTASRReportVo=new VoiceRTASRReportVo();
        voiceRTASRReportVo.setRole(role);
        voiceRTASRReportVo.setWordId(UUIDs.randomBase64UUID());
        voiceRTASRReportVo.setBegin_offset("0");
        voiceRTASRReportVo.setEnd_offset("0");
        voiceRTASRReportVo.setOut_id(sessionId);
        voiceRTASRReportVo.setWords(voiceText);
        //呼入语音识别信息不推送客户端
        if(!Constants.IN_COME.toString().equals(callType)){
            try {
                pushDataToBrowserBiz.pushAsrToBrowser(voiceRTASRReportVo, com.hfzy.ihk.facade.freeswitchCall.enums.CallTypeEnum.FREESWITCH_CALL.getValue());
            }catch (Exception e){
                logger.error("--------->推送识别结果到浏览器异常，sessionId:{}",voiceRTASRReportVo.getOut_id(),e);
            }
        }
        return voiceRTASRReportVo;
    }
}